﻿//========================================================= 
/**@file JniCommandWorker.h
 * @brief 支持Jni回调的CommandWorker
 * 
 * @date 2017-07-04   14:40:31
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_JNICOMMANDWORKER_H_
#define _LIBZHOUYB_JNICOMMANDWORKER_H_
//--------------------------------------------------------- 
#include "CommandWorker.h"

#include "../../../extension/ability/JniInvoker.h"
using zhou_yb::extension::ability::JniConverter;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace driver {
namespace async {
//--------------------------------------------------------- 
/**
 * @brief 支持使用JNI方式调用的驱动
 * @date 2017-07-04 21:13
 */
template<class TArgParser>
class JniCommandWorker : public CommandWorker
{
protected:
    TArgParser _parser;
    Ref<ComplexCommand> _createJni;
    Ref<ComplexCommand> _disposeJni;
    JNIEnv* _env;
    jobject _jobj;

    async_mutex _jniMutex;
    string _currentCmd;

    void _EnvArg(IStringArguments& jniArg, JNIEnv* env, jobject jobj)
    {
        jniArg.Put("JNIEnv", ArgConvert::ToString<pointer>(reinterpret_cast<pointer>(env)));
        jniArg.Put("jobject", ArgConvert::ToString<pointer>(reinterpret_cast<pointer>(jobj)));
    }
    jboolean _PutErr(DeviceError::ErrorCode err, JNIEnv* env, jstring sModule, jstring sCmd, ByteBuilder& sRecv, const char* currentCmd = NULL)
    {
        JniConverter cvt(env);
        string module = cvt.get_string(sModule);
        string cmd = cvt.get_string(sCmd);
        bool isOK = _HandleErr(err, module.c_str(), cmd.c_str(), currentCmd);
        if(isOK)
        {
            return JNI_TRUE;
        }
        StringArguments arg;
        StringArguments rlt;
        LastError(arg, rlt);
        _parser.ToString(rlt, sRecv);
        return JNI_FALSE;
    }
public:
    JniCommandWorker() : CommandWorker()
    {
        _env = NULL;
        _jobj = NULL;

        IsAutoHandleENV = true;
    }
    virtual ~JniCommandWorker()
    {
        DisposeENV();
    }
    /// 是否需要自动重新初始化Env和jobject
    bool IsAutoHandleENV;
    /**
     * @brief 初始化JNI
     * @date 2017-07-04 19:55
     *
     * @param [in] jniOpen [default:NULL] 初始化JNI调用的指令,默认为: JniEnvCreate
     * @param [in] jniClose [default:NULL] 释放JNI调用的指令,默认为: JniEnvDispose
     */
    bool InitJNI(const char* jniOpen = NULL, const char* jniClose = NULL)
    {
        ASYNC_LOCK(_jniMutex);
        LOG_FUNC_NAME();
        // 查找 Jni::Open 和 Jni::Close 命令
        const char* openCmd = _is_empty_or_null(jniOpen) ? "JniEnvCreate" : jniOpen;
        const char* closeCmd = _is_empty_or_null(jniClose) ? "JniEnvDispose" : jniClose;

        LOGGER(_log << "JniOpen: <" << openCmd << ">\n");
        LOGGER(_log << "JniClose:<" << closeCmd << ">\n");

        _createJni = CommandInstaller::LookUp(*this, openCmd);
        _disposeJni = CommandInstaller::LookUp(*this, closeCmd);
        if(_createJni.IsNull() || _disposeJni.IsNull())
        {
            _createJni.Free();
            _disposeJni.Free();
            return _logRetValue(false);
        }
        return _logRetValue(true);
    }
    /**
     * @brief 初始化JNI_ENV
     * @date 2017-12-21 17:03
     */
    bool InitENV(JNIEnv* env, jobject jobj)
    {
        ASYNC_LOCK(_jniMutex);
        LOG_FUNC_NAME();
        ASSERT_FuncErrRet(!_createJni.IsNull(), DeviceError::ArgIsNullErr);
        StringArguments jniArg;
        StringArguments jniRlt;
        _EnvArg(jniArg, env, jobj);
        bool bOK = _createJni->OnCommand(jniArg, jniRlt);
        if(bOK)
        {
            _env = env;
            _jobj = jobj;
        }
        return _logRetValue(bOK);
    }
    /**
     * @brief 返回是否单独初始化过JNI_ENV
     * @date 2017-12-21 17:09
     */
    inline bool IsInitENV() const
    {
        return _env != NULL && _jobj != NULL;
    }
    /**
     * @brief 释放JNI_ENV
     * @date 2017-12-21 17:04
     */
    bool DisposeENV()
    {
        ASYNC_LOCK(_jniMutex);
        LOG_FUNC_NAME();
        // 不需要再释放
        if(!IsInitENV())
            return _logRetValue(true);
        ASSERT_FuncErrRet(!_disposeJni.IsNull(), DeviceError::ArgIsNullErr);
        StringArguments jniArg;
        StringArguments jniRlt;
        _EnvArg(jniArg, _env, _jobj);
        bool bOK = _disposeJni->OnCommand(jniArg, jniRlt);
        _env = NULL;
        _jobj = NULL;
        return _logRetValue(bOK);
    }
    /**
     * @brief 调用命令(带锁)
     * @date 2017-07-05 17:04
     */
    jboolean OnCall(JNIEnv *env, jobject jobj, jstring sModule, jstring sCmd, jstring sArg, ByteBuilder& sRecv)
    {
        LOG_FUNC_NAME();
        if(!_mutex.try_lock())
        {
            return _PutErr(DeviceError::DevIsBusyErr, env, sModule, sCmd, sRecv, "Worker");
        }
        JniConverter cvt(env);
        string module = cvt.get_string(sModule);
        string cmd = cvt.get_string(sCmd);

        LOGGER(_log << "Module:<" << module << ">\n";
        _log << "Command:<" << cmd << ">\n");

        DeviceError::ErrorCode err = DeviceError::UnKnownErr;
        bool bOK = false;
        bool isExcept = false;
        CommandDispatcher* pDispatcher = _Find(module.c_str(), cmd.c_str(), &isExcept);

        StringArguments cmdArg;
        StringArguments cmdRlt;
        ResetError(cmdArg, cmdRlt);

        if(pDispatcher != NULL)
        {
            StringArguments jniArg;
            StringArguments jniRlt;

            if(IsAutoHandleENV && !isExcept && !_createJni.IsNull())
            {
                LOGGER(_log.WriteLine("TryCall:JniCreate"));
                _EnvArg(jniArg, env, jobj);

                jboolean jniOK = JNI_TRUE;
                if(!_jniMutex.try_lock())
                {
                    // JNI已经被占用
                    jniOK = _PutErr(DeviceError::DevIsBusyErr, env, sModule, sCmd, sRecv, _currentCmd.c_str());
                }
                else
                {
                    err = pDispatcher->TryCall(_createJni, jniArg, jniRlt);
                    if(err != DeviceError::Success)
                    {
                        jniOK = _PutErr(err, env, sModule, sCmd, sRecv, pDispatcher->Current().c_str());
                    }
                }
                if(jniOK != JNI_TRUE)
                {
                    _jniMutex.unlock();
                    _mutex.unlock();
                    return jniOK;
                }
            }
            string arg = cvt.get_string(sArg);
            _parser.Parse(arg.c_str(), cmdArg);
            LOGGER(_log << "TryCall:" << cmd << ">\n");
            if(!isExcept)
            {
                // 保存上次操作JNI的命令名称
                _currentCmd = module;
                if(module.length() > 0)
                {
                    _currentCmd += "::";
                }
                _currentCmd += cmd;
            }
            _mutex.unlock();
            err = pDispatcher->TryCall(cmd.c_str(), cmdArg, cmdRlt);
            _mutex.lock();
            LOGGER(_log << cmd << "->Call:<" << DeviceError::ToString(err) << ">\n");
            bOK = DeviceError::IsSuccess(err);
            if(err == DeviceError::OperatorErr)
            {
                LOGGER(_log.WriteLine("TryCall:LastError"));
                pDispatcher->TryCall("LastError", cmdArg, cmdRlt);
                // 发生错误时,底层没有正常设置错误码
                if(DeviceError::IsSuccess(cmdRlt["CODE"].To<int>(err)))
                {
                    LOGGER(_log.WriteLine("Reset LastError=OperatorErr"));
                    StringArguments::Set(cmdRlt, "CODE", ArgConvert::ToString(err));
                    StringArguments::Set(cmdRlt, "MSG", FormatErr(err, module.c_str(), cmd.c_str()));
                }
            }
            else if(!bOK)
            {
                _HandleErr(err, module.c_str(), cmd.c_str(), pDispatcher->Current().c_str());
                LastError(cmdArg, cmdRlt);
            }
            string sCode = "";
            if(!cmdRlt.Get("CODE", sCode))
            {
                LOGGER(_log << "Put CODE:<" << DeviceError::ToString(err) << ">\n");
                cmdRlt.Put("CODE", ArgConvert::ToString(static_cast<int>(err)));
            }
            if(IsAutoHandleENV && !isExcept && !_disposeJni.IsNull())
            {
                LOGGER(_log.WriteLine("TryCall:JniDispose"));
                pDispatcher->TryCall(_disposeJni, jniArg, jniRlt);
                _currentCmd = "";
                _jniMutex.unlock();
            }
        }
        else
        {
            _HandleErr(DeviceError::ArgRangeErr, module.c_str(), cmd.c_str());
            LastError(cmdArg, cmdRlt);
        }
        _parser.ToString(cmdRlt, sRecv);
        _mutex.unlock();
        return bOK ? JNI_TRUE : JNI_FALSE;
    }
};
//--------------------------------------------------------- 
} // namespace async
} // nemespace driver
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_JNICOMMANDWORKER_H_
//========================================================= 